﻿using UnityEngine;
using System.Collections;
using Assets.components;
using DiamondSquare;
using System;
using Assets.entities;

[RequireComponent(typeof(VoxelWorld))]
[RequireComponent(typeof(VoxelPrototypeFactory))]
public class IslandGenerator : MonoBehaviour {

    public Vector3 offset;
    private int[,] _matrix;
    private int _maxValue;

	void Start () {
        this.createIsland();
	}

    public void createIsland(){
        /*
        this._matrix = new int[5, 5]{
            {1, 1, 1, 1, 1},
            {2, 4, 4, 2, 1},
            {3, 4, 4, 2, 1},
            {2, 2, 1, 2, 1},
            {1, 1, 2, 2, 2},
        };*/

        /*
        this._matrix = new int[7, 16]{
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
        {1, 4, 4, 4, 1, 4, 1, 4, 1, 4, 4, 4, 4, 4, 4, 1},
        {1, 1, 4, 1, 1, 4, 1, 4, 1, 1, 4, 1, 4, 1, 1, 1},
        {1, 1, 4, 1, 1, 4, 1, 4, 1, 1, 4, 1, 4, 4, 4, 1},
        {1, 1, 4, 1, 1, 4, 1, 4, 1, 1, 4, 1, 1, 1, 4, 1},
        {1, 1, 4, 1, 1, 4, 4, 4, 1, 1, 4, 1, 4, 4, 4, 1},
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
        };
        */

        this._matrix = new int[7, 25]{
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
        {1, 4, 4, 4, 1, 4, 1, 1, 1, 4, 4, 4, 1, 4, 4, 1, 4, 1, 4, 4, 4, 4, 1, 4, 1},
        {1, 4, 1, 4, 1, 4, 1, 1, 1, 4, 1, 4, 1, 4, 4, 4, 4, 1, 1, 1, 4, 4, 1, 4, 1},
        {1, 4, 4, 4, 1, 4, 1, 1, 1, 4, 1, 4, 1, 4, 1, 4, 4, 1, 1, 4, 4, 1, 1, 4, 1},
        {1, 4, 1, 4, 1, 4, 1, 1, 1, 4, 1, 4, 1, 4, 1, 1, 4, 1, 4, 4, 1, 1, 1, 1, 1},
        {1, 4, 1, 4, 1, 4, 4, 4, 1, 4, 4, 4, 1, 4, 1, 1, 4, 1, 4, 4, 4, 4, 1, 4, 1},
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
        };

        this._maxValue = 6;
        
        this.buildWorld();
    }

    private void buildWorld() {
        VoxelWorld world = this.GetComponent<VoxelWorld>();
        VoxelPrototypeFactory factory = this.GetComponent<VoxelPrototypeFactory>();
        VoxelPrototype grassyPrototype = factory.getPrototype(0);
        VoxelPrototype muddyPrototype = factory.getPrototype(1);
        VoxelPrototype sandyPrototype = factory.getPrototype(2);
        VoxelPrototype waterPrototype = factory.getPrototype(3);
        int offsetX = Mathf.CeilToInt(this.offset.x);
        int offsetY = Mathf.CeilToInt(this.offset.y);
        int offsetZ = Mathf.CeilToInt(this.offset.z);

        //itero sui punti di controllo dello spazio tridimensionale
        //rappresentati dal contenuto bidimensionale della diamond square matrix.
        for (int x = 0; x < this._matrix.GetLength(0)-1; x++) {
            for (int z = 0; z < this._matrix.GetLength(1)-1; z++) {
                int yMax = this.getNormalizedValueAt(x, z);

                //andrebbe limitato, questo for, per la massima altezza
                for (int y = this._maxValue; y >= 0; y--) {
                    MarchingCube cube = this.getMarchingCube(x, y, z);
                    VoxelData data = new VoxelData(cube, null);
                    VoxelPrototype currentPrototype;
                    Voxel currentVoxel;

                    if (cube.isIsosurface) {
                        currentPrototype = grassyPrototype;
                    } else {
                        currentPrototype = muddyPrototype;
                    }
                    currentVoxel = currentPrototype.instantiateVoxel(data);

                    //cambio del sistema di coordinate da matriciale a spazio vettoriale 3D
                    //x identifica la riga, z la colonna di una matrice
                    //f(x,z) = (z,y,x)
                    int xCoord = offsetX + z;
                    int yCoord = offsetY + y;
                    int zCoord = offsetZ - x;
                    world.setVoxelAt(currentVoxel, xCoord, yCoord, zCoord);

                    //print("voxel at ("+xCoord+", "+yCoord+", "+zCoord+") has bitmask "+cube.ToString());
                }
            }
        }

        //tutti i voxel del mondo di gioco sono stati aggiunti al voxel engine
        //lancio la poligonizzazione complessiva.
        //la callback procederà allo spam degli elementi casuali sulla superficie.
        world.update();
    }



    /// <summary>
    /// Restituisce il marching cube associato alle coordinate stabilite.
    /// le coordinate x, y e z costituiscono il vertice posteriore sinistro
    /// sulla facciata inferiore del cubo
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="z"></param>
    /// <returns></returns>
    private MarchingCube getMarchingCube(int x, int y, int z) {
        int yMax1 = this.getNormalizedValueAt(x, z);
        int yMax2 = this.getNormalizedValueAt(x, z+1);
        int yMax3 = this.getNormalizedValueAt(x+1, z+1);
        int yMax4 = this.getNormalizedValueAt(x+1, z);

        bool y1 = (yMax1 - y) > 0;
        bool y2 = (yMax2 - y) > 0;
        bool y3 = (yMax3 - y) > 0;
        bool y4 = (yMax4 - y) > 0;
        bool y5 = (yMax1 - y + 1) > 0;
        bool y6 = (yMax2 - y + 1) > 0;
        bool y7 = (yMax3 - y + 1) > 0;
        bool y8 = (yMax4 - y + 1) > 0;
        
        return new MarchingCube(y1, y2, y3, y4, y5, y6, y7, y8);
    }



    /// <summary>
    /// Restituisce il valore normalizzato, alle posizioni x e y, della diamond square.
    /// La normalizzazione moltiplica il valore per il fattore di altezza e lo trasla di 1.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="z"></param>
    /// <returns>il valore normalizzato</returns>
    private int getNormalizedValueAt(int x, int z) {
        return this._matrix[x, z];
    }
    
}